package no.ffi.gismoidm.pubsub;

import java.io.Serializable;
import java.security.PublicKey;
import java.util.Properties;
import java.util.List;
import no.ffi.gismoidm.attrex.BooleanOperand;
import no.ffi.gismoidm.attrex.TrueAlways;
import no.ffi.gismoidm.utils.IdentityStatement;
import no.ffi.gismoidm.utils.GismoIdmException;
import no.ffi.tpm.crypto.CryptoOperations;
import no.ffi.tpm.crypto.SignedObject;

/**
 * Representation of a publication, which contains the actual publication object,
 * the identity statement of the publisher, the subscription requirements and the
 * publisher signature
 * 
 * @author anders
 */
public class Publication implements java.io.Serializable {
    private IdentityStatement publisherIS;
    private long timeCreated; // For purpose of duplicate detection
    SignedObject sContent; // contains a signed SignContent instance
    
    
    private class SignContent implements Serializable {
        BooleanOperand subscriberRequirements;
        List publicationTopics;
        Serializable publicationObject;
    }
    
    public Publication(Serializable object, List publicationTopics, BooleanOperand subReq, CryptoOperations crOp) {
       SignContent content = new SignContent();
       content.publicationObject = object;
       content.publicationTopics = publicationTopics;
       if (subReq == null) subReq = new TrueAlways();
       content.subscriberRequirements = subReq;
       try {
            sContent = crOp.sign(content);
        } catch (Exception e) 
            { e.printStackTrace(); }
        publisherIS = null; // Is added by the message router
        timeCreated = (int)(Math.random() * 10000000); //System.currentTimeMillis();
    }
    
//    public Publication(Serializable object,String topic) {
//        this(object,Collections.singletonList(topic),null,null);
//    }

    // This method is only for early testing purposes
//    public Publication(Serializable object, List publicationTopics, Properties pubAttrs, Evaluable subReq) {
//        publicationObject = object;
//        this.publicationTopics = publicationTopics;
//        publisherIS = null;
//        if (subReq == null) subReq = new TrueAlways();
//        subscriberRequirements = subReq;
//    }
    
    public void setIdentityStatement(IdentityStatement is) { 
        publisherIS = is;
    }

    public Serializable getPublicationObject() throws GismoIdmException {
        // Verify signature and extract object
        try {
            SignContent content = (SignContent)sContent.getObject();
            return content.publicationObject;
        } catch (Exception e) {throw new GismoIdmException(e.getMessage());}
    }
    
    public boolean verifySignature(PublicKey trustAnchor) throws GismoIdmException {
        // Verify signature and extract object
        System.out.println("Verifying signature of publication");

        // Signer's public key is in identity statement
        publisherIS.validateIdentityStatement(trustAnchor);
        PublicKey signerKey = publisherIS.getSubjectPublicKey();
        try {
            return CryptoOperations.verify(sContent, signerKey);
        } catch (Exception e) 
            { throw new GismoIdmException(e.getMessage());}
    } 
    
    public long getTimeCreated() { return timeCreated; }
    public List getPublicationTopics() {
        try {
            SignContent content = (SignContent)sContent.getObject();
            return content.publicationTopics;
        } catch (Exception e) {e.printStackTrace();}
        return null;
    }
    
    public BooleanOperand getSubscriberRequirements() {
        try {
            SignContent content = (SignContent)sContent.getObject();
            return content.subscriberRequirements;
        } catch (Exception e) {e.printStackTrace();}
        return null;
    }

    public IdentityStatement getIdentityStatement() { return publisherIS; }
    public Properties getPublisherAttributes() { return publisherIS.getAttributeAsProperties(); }
    
    public String toString() {
       SignContent content=null;
       StringBuffer sb = new StringBuffer();
       try {
           content = (SignContent)sContent.getObject();
        } catch (Exception e) {e.printStackTrace();}
       sb.append(content.subscriberRequirements.toString()); sb.append("\n");
       sb.append(content.publicationTopics);                 sb.append("\n");
       sb.append(publisherIS);                               sb.append("\n");
       sb.append(content.publicationObject);
       return new String(sb);
       
    }
    
}
